package fr.jibhaine.comp
{
	import flash.display.GradientType;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.core.UIComponent;
	
	/**
	 * Cette classe est un potentiomètre rotatif, mieux qu'un slider, complètement adapté aux applications audio.
	 * */
	public class RotaryKnob extends UIComponent
	{
		/**
		 * Minimal Knob Value
		 */
		private var _minValue:Number = 0 ;
		public function set minValue(value:Number):void{
			_minValue = value;	
		}
		public function get minValue():Number{
			return _minValue;
		}
		
		/**
		 * Maximal Knob Value
		 */
		private var _maxValue:Number = 100;
		public function set maxValue(val:Number):void{
			_maxValue = val;
		}
		public function get maxValue():Number{
			return _minValue;
		}
		
		/**
		 * position (angle) of the minimum value  
		 */
		private var _minRotation:Number = 0;
		public function set minRotation(val:Number):void{
			_minRotation = val;
		}
		public function get minRotation():Number{
			return _minRotation;
		}
		
		/**
		 * position (angle) of the maximum value  
		 */
		private var _maxRotation:Number = 270;
		public function set maxRotation(val:Number):void{
			_maxRotation = val;
		}
		public function get maxRotation():Number{
			return _maxRotation;
		}
		
		private var _currentRotation:Number = 0;
		public function set currentRotation(val:Number):void{
			_currentRotation = val;
		}
		public function get currentRotation():Number{
			return _currentRotation;
		}
		
		
		/**
		 * Current value calculated from the rotation angle, min and max rotation and their values
		 */
		
		private var _value:Number = 0;
		
		 /**
		 * Set the value of the knob
		 *  @param value
	     * 
	     */
	     [Bindable]
	    public function set value(val:Number):void {
	    	trace("Setting value : "+val)
	        if (val != _value && !isNaN(val)) {
	            _value = val;
	            _valueChanged = true;
	            invalidateProperties();
	            invalidateDisplayList();
	        }
	        
	    }
	    
	    public function get value():Number {
	    	return _value;
	    }
	    
		private var _valueChanged:Boolean=false;
		
		/**
		 * Si mis à vrai, le potard se dirige vers la position de la souris a l'écran,
		 * sinon il tourne vers la droite quand la souris est a droite et inversement
		 */
		public var horizontalRotation:Boolean=false;
		
		public var clockwise:Boolean=true;
		
		private var _color:uint = 0xFFFFFF;
		public function set color(val:uint):void{		_color = val;	}
		public function get color():uint{			return _color;	}
		
		
		/**
		 * Constructor
		 */
		public function RotaryKnob()
		{
			super();
			this.addEventListener(MouseEvent.CLICK , mouseEventHandler);
        	this.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler);
        	this.addEventListener(MouseEvent.MOUSE_UP,     mouseEventHandler); 
		}
		
		
		private function rad2degree(rads:Number):Number {
			var n:Number = (180 * rads)/Math.PI
			trace(rads+" radians = "+n+" degrees");
     		return n;
		}
		private function degree2rad(deg:Number):Number{
			var n:Number = (Math.PI*deg)/180;
			trace(deg+" degrees = "+n+" radians");
			return n;
		}	
		
	    /**
	     * gets Rotations from Mouse-Position 
	     * @param event
	     * @return Rotation
	     * 
	     */
	    private function calculateRotation(mouseEvent:MouseEvent):Number {
			var cp:Point = new Point (this.x+this.width/2, this.y+this.height/2);
	        cp = this.parent.localToGlobal(cp);
	        var mp:Point = new Point(mouseEvent.stageX, mouseEvent.stageY);
	        var dp:Point = cp.subtract(mp);
	        var angle:Number = Math.atan2(dp.y, dp.x);
	        var rot:Number = rad2degree(angle);
	        
	        if (rot > _maxRotation || rot < -90) {
	            rot = _maxRotation;
	        } else if (rot < _minRotation && rot > -90) {
	            rot = _minRotation;
	        }
	        return rot;
	    }
	    
	    /**
	     * Calculates the knobValue based on rotation and range 
	     * @param rotation current Rotation of the knob
	     * @return the value of the knob
	     * 
	     */
	    private function calculateValue(rotation:Number):Number {
	        return (rotation - _minRotation) / (_maxRotation - _minRotation) * (_maxValue - _minValue) + _minValue;
	    }
	    /** Mouse Events Handlers */
	    
	    protected function mouseEventHandler(event:Event):void
	    {
	        var mouseEvent:MouseEvent = event as MouseEvent;
	           switch (mouseEvent.type) {
	               case MouseEvent.MOUSE_DOWN:     mouseDown();                                 break;
	               case MouseEvent.MOUSE_UP:         mouseUp();                                     break;
	           }
	        this.toolTip = String(_value);
	    }
    
	    protected function mouseMoveHandler(event:Event):void
	    {
	        var mouseEvent:MouseEvent = event as MouseEvent;
	        _currentRotation = calculateRotation(mouseEvent);
	        _value = calculateValue(_currentRotation);
	        trace("mouseMove ("+mouseEvent.stageX+","+mouseEvent.stageY+")");
	    }
    
	    protected function mouseDown():void {
	    	trace("mouseDown");
	        addMoveHandlers();
	    }
			
	    protected function mouseUp():void {
	    	trace("mouseUp");
	        removeMoveHandlers();
	    }
	    
	    protected function addMoveHandlers():void {
	        systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
	        systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler);
	        
	        //Add these if you want to get Events from other security sandboxes
	        //systemManager.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_MOVE_SOMEWHERE, mouseMoveHandler);
	        //systemManager.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseEventHandler);
	    }
	    
	    protected function removeMoveHandlers():void {
	        systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
	        systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_UP, mouseEventHandler);
	        
	        //Add these to remove sandbox events. See above
	        //systemManager.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_MOVE_SOMEWHERE, mouseMoveHandler);
	        //systemManager.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseEventHandler);
	    }
		/**  Component-Lifecycle Methods */
	    override protected function commitProperties():void {
	        super.commitProperties();
	        
	        if (_valueChanged) {
	            _currentRotation = (_value - _minValue)/(_maxValue - _minValue) * (_maxRotation - _minRotation);
	            trace("commitProperties set currentRotation : "+_currentRotation);
	            _valueChanged = false;
	        }
	        if (_minRotation > _currentRotation) {
	             _currentRotation = _minRotation.valueOf();
	        }
	        if (_maxRotation < _currentRotation) {
	            _currentRotation = _maxRotation.valueOf();
	        }
	    }
		
		/**
		 * Draw the knob using the properties
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
		    graphics.clear();
		    // Define line style
    		graphics.lineStyle(1, _color, 1.0);
    		graphics.beginGradientFill(GradientType.RADIAL,[0x999999,0x333333],[1,1],[0,0xFF]);
			// Calculate the center point
    		var xCenter:Number = unscaledWidth/2;
    		var yCenter:Number = unscaledHeight/2;
    		
    		// Calculate circle Radius
    		var circleRadius:Number;
    		if(unscaledWidth<unscaledHeight) 	circleRadius = (unscaledWidth-10)/2;
    		else    							circleRadius = (unscaledHeight-10)/2;
    		
    		graphics.drawCircle(xCenter, yCenter, circleRadius);      
			trace('minRotation : '+_minRotation);  
			trace('currentRotation : '+_currentRotation);
			trace('maxRotation : '+_maxRotation);
			//calculate the arrow point
			var xEnd:Number = Math.cos(degree2rad(_minRotation+_currentRotation))*(circleRadius+5) + xCenter;
			var yEnd:Number = -Math.sin(degree2rad(_minRotation+_currentRotation))*(circleRadius+5) + yCenter;
			
			graphics.moveTo(xCenter,yCenter);
			graphics.lineTo(xEnd,yEnd);
			trace('Drawing knob mark at '+(_minRotation+_currentRotation)+'°');
			trace('=> line from ('+xCenter+','+yCenter+') to ('+xEnd+','+yEnd+')');
		}
	}
}